package repo

import (
	"gitee.com/mkwchecking/sr-core/domain/dao"
	"gitee.com/mkwchecking/sr-core/domain/dao/scope"
	"gitee.com/mkwchecking/sr-core/storage/cache"
	"gitee.com/mkwchecking/sr-core/storage/database"
	"gorm.io/gorm"
)

func New[Ent any](db *gorm.DB, cache cache.ICache) *Repo[Ent] {
	return &Repo[Ent]{
		dao:   dao.NewDao[Ent](db),
		cache: cache,
	}
}

func NewSet[Ent any](dataset database.IDataSet, cache cache.ICache) *Repo[Ent] {
	orm, ok := dataset.GetDefault()
	if !ok {
		return nil
	}

	return &Repo[Ent]{
		dao:   dao.NewDao[Ent](orm),
		cache: cache,
	}
}

var _ IRepo[any] = (*Repo[any])(nil)

type Repo[Ent any] struct {
	dao   dao.IDao[Ent]
	cache cache.ICache
}

// Dao implements IRepo.
func (r *Repo[Ent]) Dao() dao.IDao[Ent] {
	return r.dao
}

// EntBuilder implements IRepo.
func (r *Repo[Ent]) EntBuilder(f func(*Ent)) scope.IScope {
	ent := new(Ent)
	f(ent)
	return scope.Where(ent)
}

// Find implements IRepo.
func (r *Repo[Ent]) Find(cds ...any) (*Ent, error) {
	return r.dao.FindOne(cds...)
}

// FindWithPK implements IRepo.
func (r *Repo[Ent]) FindWithPK(id any, cds ...any) (*Ent, error) {
	nCds := append(cds, scope.WithPKIn(id))
	return r.dao.FindOne(nCds...)
}

// FindMulti implements IRepo.
func (r *Repo[Ent]) FindMulti(pageNum, pageSize int, cds ...any) ([]*Ent, int64, error) {
	nCds := append(cds, scope.WithPage(pageNum, pageSize, 0))
	return r.dao.FindMulti(nCds...)
}

// FindMultiWithPK implements IRepo.
func (r *Repo[Ent]) FindMultiWithPK(ids []any, cds ...any) ([]*Ent, int64, error) {
	nCds := append(cds, scope.WithPKIn(ids))
	return r.dao.FindMulti(nCds...)
}

// FindWithEnt implements IRepo.
func (r *Repo[Ent]) FindWithEnt(ent any, cds ...any) (*Ent, error) {
	nCds := append(cds, scope.Search(ent))
	return r.dao.FindOne(nCds...)
}

// FindMultiWithEnt implements IRepo.
func (r *Repo[Ent]) FindMultiWithEnt(pageNum, pageSize int, ent any, cds ...any) ([]*Ent, int64, error) {
	nCds := append(cds,
		scope.Search(ent),
		scope.WithPage(pageNum, pageSize, 0),
	)
	return r.dao.FindMulti(nCds...)
}

// Update implements IRepo.
func (r *Repo[Ent]) Update(ent *Ent, cds ...any) (*Ent, error) {
	return r.dao.Update(ent, cds...)
}

// UpdateWithEnt implements IRepo.
func (r *Repo[Ent]) UpdateWithEnt(ent *Ent, where any, cds ...any) (*Ent, error) {
	nCds := append(cds, scope.Search(where))
	return r.dao.Update(ent, nCds...)
}

// Create implements IRepo.
func (r *Repo[Ent]) Create(ent *Ent, cds ...any) (*Ent, error) {
	return r.dao.Create(ent, cds...)
}

// CreateMulti implements IRepo.
func (r *Repo[Ent]) CreateMulti(ents []*Ent, cds ...any) (int64, error) {
	return r.dao.CreateMulti(ents, cds...)
}

// SoftDeleteWithPK implements IRepo.
func (r *Repo[Ent]) SoftDeleteWithPK(id any, cds ...any) error {
	nCds := append(cds, scope.WithPKIn(id))
	return r.dao.SoftDelete(nCds...)
}

// SoftDeleteWithEnt implements IRepo.
func (r *Repo[Ent]) SoftDeleteWithEnt(where any, cds ...any) error {
	nCds := append(cds, scope.Search(where))
	return r.dao.SoftDelete(nCds...)
}

// ForceDeleteWithPK implements IRepo.
func (r *Repo[Ent]) ForceDeleteWithPK(id any, cds ...any) error {
	nCds := append(cds, scope.WithPKIn(id))
	return r.dao.ForceDelete(nCds...)
}

// ForceDeleteWithEnt implements IRepo.
func (r *Repo[Ent]) ForceDeleteWithEnt(where any, cds ...any) error {
	nCds := append(cds, scope.Search(where))
	return r.dao.ForceDelete(nCds...)
}

func (r *Repo[Ent]) Association(model *Ent, column string, cds ...any) (dao.IAssociation, error) {
	return r.dao.Association(model, column, cds...)
}
